package com.system.common.util;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 高并发唯一订单号生成器
 */
public class OrderNoGenerator {

    private final StringRedisTemplate redisTemplate;

    // 雪花算法参数
    private final long workerId;      // 机器ID (0~31)
    private final long datacenterId;  // 数据中心ID (0~31)
    private final AtomicLong sequence = new AtomicLong(0L);

    private final long twepoch = 1288834974657L;
    private long lastTimestamp = -1L;

    public OrderNoGenerator(StringRedisTemplate redisTemplate, long workerId, long datacenterId) {
        this.redisTemplate = redisTemplate;
        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }

    /**
     * 核心方法：生成唯一订单号
     */
    public String generateOrderNo(Long userId) {
        // 日期部分
        String date = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));

        // Redis每日自增流水号
        String key = "order:" + date;
        Long incr = redisTemplate.opsForValue().increment(key, 1);
        redisTemplate.expire(key, java.time.Duration.ofDays(2)); // 避免key无限增长

        // 雪花ID部分
        long snowflakeId = nextId();

        // 拼接订单号：日期 + 流水号(6位) + 雪花末6位 + 用户ID后3位
        return date
                + String.format("%06d", incr)
                + (snowflakeId % 1000000)
                + String.format("%03d", userId % 1000);
    }

    /**
     * 雪花算法生成唯一ID
     */
    private synchronized long nextId() {
        long timestamp = System.currentTimeMillis();

        if (timestamp < lastTimestamp) {
            throw new RuntimeException("Clock moved backwards. Refusing to generate id.");
        }

        if (lastTimestamp == timestamp) {
            long seq = (sequence.incrementAndGet()) & 4095; // 12位序列号
            if (seq == 0) {
                // 序列号溢出，等待下一毫秒
                timestamp = waitNextMillis(lastTimestamp);
            }
        } else {
            sequence.set(0L);
        }

        lastTimestamp = timestamp;

        return ((timestamp - twepoch) << 22)
                | (datacenterId << 17)
                | (workerId << 12)
                | sequence.get();
    }

    private long waitNextMillis(long lastTimestamp) {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }

}
